﻿// Copyright (c) 2014 Morten Bakkedal
// This code is published under the MIT License.

using System;
using System.Diagnostics;

using FuncLib.Functions;
using FuncLib.Functions.Compilation;

namespace FuncLibSamples.Samples
{
	public static class HelloWorld
	{
		public static void Sample1()
		{
			// Variables.
			Variable x = new Variable();
			Variable y = new Variable();

			// Define function.
			Function f = x * Function.Exp(x * y);

			// Compute second order partial derivative.
			Function g = f.Derivative(x).Derivative(y);

			// Evaluate function for (x, y) = (2, 3).
			Console.WriteLine(g.Value(x | 2.0, y | 3.0));

			// Or without operator overloading.
			Console.WriteLine(g.Value(new Point(new VariableAssignment(x, 2.0), new VariableAssignment(y, 3.0))));

			// Compile to IL code using the variables given.
			CompiledFunction h = Compiler.Compile(g, x, y);

			// Evaluate again. Now the order of the variables is fixed like an ordinary C# method.
			Console.WriteLine(h.Value(2.0, 3.0));

			// A matrix with mixed functions and constants.
			FunctionMatrix a = new FunctionMatrix(new Function[,] { { f, g }, { 1.0, f * g } });

			// Compute inverse and evaluate derivative of first entry.
			Console.WriteLine(FunctionMatrix.Inverse(a)[0, 0].Derivative(x).Value(x | 2.0, y | 3.0));

			// Use of PartialValue.
			Console.WriteLine(g.Value(x | 2.0, y | 3.0));
			Console.WriteLine(g.PartialValue(x | 2.0).Value(y | 3.0));
			Console.WriteLine(g.PartialValue(x | 2.0, y | 3.0).Value());
			Console.WriteLine(g.PartialValue(x | 2.0).Derivative(x).Value(y | 3.0));
			//g.Substitute(x | 2.0, y | g);
		}

		public static void Sample2()
		{
			// Variables.
			Variable x = new Variable();
			Variable y = new Variable();

			// Define function a complicated function.
			Function f = Function.Exp(x * y) + x + 0.5;
			f *= Function.Cos(f);
			f *= Function.Cos(f);
			f *= Function.Cos(f);
			f *= Function.Cos(f);
			f = f.Derivative(x);

			// Evaluate the usual way.
			Console.WriteLine(f.Value(x | 2.0, y | 3.0));

			// Compile the function to IL code.
			CompiledFunction g = Compiler.Compile(f, x, y);

			// Compile the function (including first and second derivatives) to IL code.
			CompiledFunction g2 = Compiler.Compile(f, new Variable[] { x, y }, 1); /* 2 */

			//g = g2;

			// Evaluate.
			Console.WriteLine(g.Value(2.0, 3.0));

			// Or we may still use the Function syntax.
			Console.WriteLine(g.Value(x | 2.0, y | 3.0));

			//return;

			// Measure performance difference.
			int n = 500000;

			Stopwatch sw1 = Stopwatch.StartNew();
			for (int i = 0; i < n; i++)
			{
				f.Value(x | 2.0, y | 3.0);
			}
			Console.WriteLine(sw1.Elapsed);

			Stopwatch sw2 = Stopwatch.StartNew();
			for (int i = 0; i < n; i++)
			{
				g.Value(2.0, 3.0);
				//Evaluation e = new Evaluation(x | 2.0, y | 3.0);
				//g.Value(e);
				//g2.Derivative(x).Value(e);
				//g2.Derivative(y).Value(e);
			}
			Console.WriteLine(sw2.Elapsed);
		}
	}
}
